package com.zang.util;

import lombok.extern.slf4j.Slf4j;
import org.openjdk.jol.info.GraphLayout;
import org.openjdk.jol.vm.VM;
import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
 * 查看对象地址
 * @author Zhang Qiang
 * @date 2019/12/11 09:59
 */
@Slf4j
public class AddressUtil {

    static final Unsafe unsafe = getUnsafe();
    static final boolean is64bit = true;

    public static void main(String[] args) {
        AddressUtil addressUtil = new AddressUtil();
        String a = "hello2";
        final String b = "hello";
        String c = "hello";
        String d = b + 2;
        String e = c + 2;
        /**
         * intern()
         * 一个Native方法，它的作用是：如果字符串常量池中已经包含一个等 于此String对象的字符串，则返回代表池中这个字符串的String对象；否则，将此String对象包含的字符串添加到常量池中，并且返回此String对象的引用。
         */
        String f = a.intern();
        log.info("b==c:{}", (b==c));
        log.info("a==d:{}", (a==d));
        log.info("a==e:{}", (a==e));

        log.info("====a========a========a====");
        addressUtil.printAddress(a);
        log.info("====b========b========b====");
        addressUtil.printAddress(b);
        log.info("====c========c========c====");
        addressUtil.printAddress(c);
        log.info("====d========d========d====");
        addressUtil.printAddress(d);
        log.info("====e========e========e====");
        addressUtil.printAddress(e);
        log.info("====f========f========f====");
        addressUtil.printAddress(f);
    }

    public void printAddress(Object... roots){
        log.info("printAddresses by jol");
        String address = GraphLayout.parseInstance(roots).toPrintable();
        String vm = VM.current().addressOf(roots)+"";
        log.info("{} vm:{}", roots.getClass().getSimpleName(), vm);
        log.info("{} address:{}", roots.getClass().getSimpleName(), address);
    }

    public static void printAddressByUnsage(String label, Object roots){
        log.info("printAddresses by unsafe");
        printAddressesByUnsafe("Address", roots);
    }

    @SuppressWarnings("deprecation")
    public static void printAddressesByUnsafe(String label, Object... objects) {
        System.out.print(label + ":         0x");
        long last = 0;
        int offset = unsafe.arrayBaseOffset(objects.getClass());
        int scale = unsafe.arrayIndexScale(objects.getClass());
        switch (scale) {
            case 4:
                long factor = is64bit ? 8 : 1;
                final long i1 = (unsafe.getInt(objects, offset) & 0xFFFFFFFFL) * factor;
                System.out.print(Long.toHexString(i1));
                last = i1;
                for (int i = 1; i < objects.length; i++) {
                    final long i2 = (unsafe.getInt(objects, offset + i * 4) & 0xFFFFFFFFL) * factor;
                    if (i2 > last){
                            System.out.print(", +" + Long.toHexString(i2 - last));
                        }
                    else {
                        System.out.print(", -" + Long.toHexString(last - i2));
                    }
                    last = i2;
                }
                break;
            case 8:
                throw new AssertionError("Not supported");
            default:
                break;
        }
        System.out.println();
    }

    private static Unsafe getUnsafe() {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            return (Unsafe) theUnsafe.get(null);
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }
}
